﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Cyss.Core
{
    /// <summary>
    /// 本地数组暂存再文件里,适用于对性能没有要求数据量不大的,访问不频繁,无法使用redis 缓存的时候使用
    /// </summary>
    public class FileCache
    {
        /// <summary>
        /// 主键集合
        /// </summary>
        private static ConcurrentDictionary<string, KeyModel> keys = new ConcurrentDictionary<string, KeyModel>();

        /// <summary>
        /// 异步任务计时器
        /// </summary>
        private static System.Threading.Timer _timer;

        /// <summary>
        /// 间隔
        /// </summary>
        private static int Interval = 1000;

        /// <summary>
        /// 是否有变更
        /// </summary>
        private static bool IsHasChange { set; get; }

        /// <summary>
        /// 最后清理时间
        /// </summary>
        private static DateTime LastClearTime { set; get; }

        static FileCache()
        {
            LoadKeys();
            _timer = new System.Threading.Timer(AsyncTask, 0, Interval, Interval);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key">主键</param>
        /// <param name="josnData">json数据</param>
        /// <param name="Expires">到期时间(分钟)</param>
        /// <returns></returns>
        public static async Task<bool> Set(string key, string josnData, int Expires = 0)
        {

            KeyModel model;
            if (ContainsKey(key))
            {
                model=keys[key];
                model.UpdateTime =DateTime.Now;
            }
            else
            {
                model = new KeyModel();
                model.Key=key;
                model.CreateTime=DateTime.Now;
                model.UpdateTime =DateTime.Now;
                model.ExpiresTime=Expires>0 ? DateTime.Now.AddMinutes(Expires) : DateTime.MaxValue;
                keys.TryAdd(key, model);
            }
            try
            {
                await Write(josnData, model);
                IsHasChange=true;
            }
            catch (Exception ex)
            {
                keys.Remove(key, out model);
            }
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key">主键</param>
        /// <param name="Data">数据</param>
        /// <param name="Expires">到期时间(分钟)</param>
        /// <returns></returns>
        public static async Task<T> Get<T>(string key)
        {
            if (!keys.ContainsKey(key))
            {
                return default(T);
            }
            var keyModel = keys[key];
            var value = await GetFileText(keyModel);
            if (value==null)
            {
                return default(T);
            }
            return JsonHelper.DeserializeObject<T>(value);
        }

        /// <summary>
        /// 删除缓存
        /// </summary>
        /// <param name="key"></param>
        public static void Delete(string key)
        {
            if (!keys.ContainsKey(key))
            {
                return;
            }
            var keyModel = keys[key];
            DeleteFileText(keyModel);
            keys.Remove(key, out keyModel);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>

        public static bool ContainsKey(string key)
        {
            return keys.ContainsKey(key);
        }

        /// <summary>
        /// 获取文件数据
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        private static async Task<string> GetFileText(KeyModel model)
        {
            var path = GetFileName(model);
            if (System.IO.File.Exists(path))
            {
                return await System.IO.File.ReadAllTextAsync(GetFileName(model));
            }
            return null;
        }

        private static void DeleteFileText(KeyModel model)
        {
            try
            {
                System.IO.File.Delete(GetFileName(model));
            }
            catch (Exception ex)
            {

            }

        }

        /// <summary>
        /// 获取缓存文件名称
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        private static string GetFileName(KeyModel model)
        {
            string path = AppDomain.CurrentDomain.BaseDirectory + @"\FileCache\";
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);

            path = path + model.CreateTime.ToString("yyyy-MM-dd");
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);

            return path +@$"\{model.Key}.json";
        }

        /// <summary>
        /// 加载缓存配置
        /// </summary>
        private static void LoadKeys()
        {
            string path = AppDomain.CurrentDomain.BaseDirectory + @"\FileCache\";
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);
            var fileName = path+"keys.json";
            if (System.IO.File.Exists(fileName))
            {
                var keyModels = JsonHelper.DeserializeObject<IEnumerable<KeyModel>>(System.IO.File.ReadAllText(fileName));
                foreach (var model in keyModels)
                {
                    keys.TryAdd(model.Key, model);
                }
            }
        }

        /// <summary>
        /// 写入文件
        /// </summary>
        /// <param name="Message"></param>
        /// <param name="FileName"></param>
        private static async Task Write(string Data, KeyModel model)
        {
            await WriteFileAsync(Data, GetFileName(model));
        }

        private static async Task WriteFileAsync(string Data, string fileFullPath)
        {
            StreamWriter sw;
            if (File.Exists(fileFullPath))
            {
                File.Delete(fileFullPath);
            }
            if (!File.Exists(fileFullPath))
            {
                sw = File.CreateText(fileFullPath);
            }
            else
            {
                sw = File.AppendText(fileFullPath);
            }
            await sw.WriteAsync(Data);
            sw.Close();
            sw = null;
        }
        private static void WriteFile(string Data, string fileFullPath)
        {
            StreamWriter sw;
            if (File.Exists(fileFullPath))
            {
                File.Delete(fileFullPath);
            }
            if (!File.Exists(fileFullPath))
            {
                sw = File.CreateText(fileFullPath);
            }
            else
            {
                sw = File.AppendText(fileFullPath);
            }
            sw.Write(Data);
            sw.Close();
            sw = null;
        }
        /// <summary>
        /// 保存key文件
        /// </summary>
        /// <returns></returns>
        private static void WriteKey()
        {
            string path = AppDomain.CurrentDomain.BaseDirectory + @"\FileCache\";
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);
            var fileName = path+"keys.json";
            WriteFile(keys.Select(s => s.Value).ToSerializeObject(), fileName);
        }

        /// <summary>
        /// 清理缓存文件
        /// </summary>
        private static void ClearCacheFile()
        {
            string path = AppDomain.CurrentDomain.BaseDirectory + @"\FileCache\";
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);

            var ckeys = keys.Where(x => x.Value.ExpiresTime<DateTime.Now);
            foreach (var item in ckeys)
            {
                path = path + item.Value.CreateTime.ToString("yyyy-MM-dd");
                var fileName = path +@$"\{item.Key}.json";
                if (System.IO.File.Exists(fileName))
                {
                    System.IO.File.Delete(fileName);
                }
                keys.TryRemove(item);
            }

        }

        /// <summary>
        /// 
        /// </summary>
        private static void AsyncTask(object state)
        {
            _timer.Change(-1, -1);
            try
            {
                if (IsHasChange)
                {
                    WriteKey();
                    IsHasChange=false;
                }
                if (LastClearTime.AddMinutes(10)<DateTime.Now)
                {
                    //10分钟清理一次文件
                    ClearCacheFile();
                    LastClearTime=DateTime.Now;
                }
            }
            catch (Exception ex)
            {

            }
            _timer.Change(Interval, Interval);
        }



        private struct KeyModel
        {
            public string Key { set; get; }

            public DateTime CreateTime { set; get; }

            public DateTime UpdateTime { set; get; }

            public DateTime ExpiresTime { set; get; }
        }
    }
}
